namespace Tekdev.InputValidation
{
    #region using
    using System;
    using System.IO;
    using System.Reflection;
    using System.Web;
    #endregion

    public class QueryStringModule : IHttpModule
    {
		#region Variables
        private HttpApplication _application;
        private Config _config;
        private string _queryStringConfig;
        private string _queryStringConfigFile;
        private string _queryStringConfigModified;
		#endregion

		#region Constructors
		public QueryStringModule()
		{

		}
		#endregion

		#region IHttpModule Methods
		public void Init(HttpApplication context)
		{
			//Set the HttpModule to handle the request event
			_application = context;
			_application.BeginRequest += new EventHandler(DoBeginRequest);

			//If nothing is set in the Application variable, then set it with the defaults
			if ((context.Application["__QueryStringConfigFile__"] == null) || (context.Application["__QueryStringConfigFile__"].ToString() == string.Empty))
			{
				_queryStringConfigFile = HttpContext.Current.Server.MapPath("~/querystring.config");
				context.Application["__QueryStringConfigFile__"] = _queryStringConfigFile;				
			}

			Load();
		}

        public void Dispose()
        {
            _application = null;
            _queryStringConfig = null;
            _queryStringConfigFile = null;
            _queryStringConfigModified = null;
            _config = null;
        }
		#endregion

        #region Event Handlers
        private void DoBeginRequest(object source, EventArgs e)
        {
            //If the file has been modified since the last request, reload it
            if (_queryStringConfigModified != File.GetLastWriteTime(_queryStringConfigFile).ToFileTime().ToString())
            {
                Load();
            }

            //Get the Url without the query string
            string url = HttpContext.Current.Request.Url.AbsoluteUri.ToLower();
            if (url.IndexOf("?") > -1)
            {
                url = url.Substring(0, url.IndexOf("?"));
            }

            //Get the path to the actual file and make sure it is a supported extension (fileTypes/add)
            string filePath = HttpContext.Current.Request.FilePath;
            string extension = Path.GetExtension(HttpContext.Current.Server.MapPath(filePath));
            if (_config.FileTypes.Contains(extension))
            {
                string currentPage = HttpContext.Current.Request.Path.ToLower();
                foreach (QueryStringPage page in _config.Pages)
                {
                    if (page.Url.IndexOf("~/") > -1)
                    {
                        string oldURL = page.Url;
                        page.Url = page.Url.Replace("~/", HttpContext.Current.Request.ApplicationPath.ToLower() + "/");
                        if (currentPage == page.Url)
                        {
                            currentPage = oldURL;
                        }
                        continue;
                    }
                    if (page.Url.IndexOf("http://") > -1)
                    {
                        string oldURL = page.Url;
                        if (url != page.Url)
                        {
                            continue;
                        }
                        currentPage = oldURL;
                    }
                }
                QueryStringPage qsPage = _config.Pages[currentPage];
                if (!QueryStringUtil.Validate(_queryStringConfig, HttpContext.Current.Request.QueryString, qsPage))
                {
                    //If the user wants to display the error
                    if (qsPage.ShowError)
                    {
                        //If they set their own Exception class to throw (IQueryStringException)
                        if (_config.CustomError.Mode == "On"
                            && _config.CustomError.ExceptionAssembly != null
                            && _config.CustomError.ExceptionAssembly != string.Empty
                            && _config.CustomError.ExceptionClass != null
                            && _config.CustomError.ExceptionClass != string.Empty)
                        {
                            Type exceptionClass = Assembly.Load(_config.CustomError.ExceptionAssembly).GetType(_config.CustomError.ExceptionAssembly + "." + _config.CustomError.ExceptionClass);
                            if (exceptionClass != null && _config.CustomError.Mode == "On")
                            {
                                //Use reflection to load their class and throw it
                                Type[] parameterTypes = new Type[] { typeof(string) };
                                object[] parameters = new object[] { _config.CustomError.ErrorMessage };
                                IQueryStringException ex = (IQueryStringException)exceptionClass.GetConstructor(parameterTypes).Invoke(parameters);
                                throw ex;
                            }

                            if (_config.CustomError.ErrorMessage != null && _config.CustomError.ErrorMessage != string.Empty)
                            {
                                throw new InvalidQueryStringException(_config.CustomError.ErrorMessage);
                            }

                            throw new InvalidQueryStringException("Invalid query string data was entered");
                        }
                        if (_config.CustomError.Mode == "On"
                            && _config.CustomError.ErrorMessage != null
                            && _config.CustomError.ErrorMessage != string.Empty)
                        {
                            throw new InvalidQueryStringException(_config.CustomError.ErrorMessage);
                        }

                        throw new InvalidQueryStringException("Invalid query string data was entered");
                    }

                    HttpContext.Current.Items["__QueryStringIsValid__"] = false;
                }
                else
                {
                    HttpContext.Current.Items["__QueryStringIsValid__"] = false;
                }
            }
        }
        #endregion

		#region Custom Methods
		private void Load()
		{
			HttpContext context = HttpContext.Current;

			//Check the config file's modified date to see when it was updated
			_queryStringConfigModified = File.GetLastWriteTime(context.Application["__QueryStringConfigFile__"].ToString()).ToFileTime().ToString();

			//Open the file and load the XML contents into the _queryStringConfig variable
			StreamReader reader = new StreamReader(context.Application["__QueryStringConfigFile__"].ToString());
			_queryStringConfig = reader.ReadToEnd();			
			reader.Close();

			//Load the Config object
			_config = QueryStringUtil.Load(_queryStringConfig);
		}
        #endregion
 
    }
}

